package com.michaelbulava.TFS.tasks.connect;

import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.michaelbulava.TFS.Messages;
import com.michaelbulava.TFS.log.FileLogger;
import com.michaelbulava.TFS.persistence.IdeaPersistenceStoreProvider;
import com.michaelbulava.TFS.tasks.BackgroundableTask;
import com.michaelbulava.TFS.ui.dialogs.connect.CredentialsDialog;
import com.michaelbulava.TFS.ui.helpers.UIHelpers;
import com.microsoft.tfs.core.TFSConnection;
import com.microsoft.tfs.core.credentials.CachedCredentials;
import com.microsoft.tfs.core.credentials.CredentialsManagerFactory;
import com.microsoft.tfs.core.exceptions.ACSUnauthorizedException;
import com.microsoft.tfs.core.exceptions.TFSUnauthorizedException;
import com.microsoft.tfs.core.httpclient.Credentials;
import com.microsoft.tfs.core.httpclient.DefaultNTCredentials;
import com.microsoft.tfs.core.util.CredentialsUtils;
import com.microsoft.tfs.core.ws.runtime.exceptions.TransportRequestHandlerCanceledException;
import com.microsoft.tfs.util.Check;

import java.net.URI;
import java.text.MessageFormat;

/**
 * Created by mbulava on 6/16/2015.
 */
public abstract class ConnectTask extends BackgroundableTask {
    private static Logger log = FileLogger.getInstance(ConnectTask.class);

    private final URI serverURI;
    private Credentials credentials;
    private TFSConnection connection;
    private boolean showErrorDialog = true;

    public ConnectTask(Project project, URI serverURI){
        this(project, serverURI, null);
    }

    public ConnectTask(Project project, URI serverURI, Credentials credentials){
        super(project, Messages.getString("ConnectTask.DefaultTitle"), true);
        Check.notNull(serverURI, "serverURI");
        this.serverURI = serverURI;
        this.credentials = credentials;
    }

    public void setShowErrorDialog(boolean showErrorDialog){
        this.showErrorDialog = showErrorDialog;
    }

    @Override
    public void run(ProgressIndicator progressIndicator) {
        if(this.credentials == null){
            CachedCredentials cachedCredentials = CredentialsManagerFactory.getCredentialsManager(IdeaPersistenceStoreProvider.INSTANCE)
                    .getCredentials(serverURI);
            this.credentials = (cachedCredentials != null) ? cachedCredentials.toCredentials() : new DefaultNTCredentials();
        }

        if (this.credentials == null || CredentialsUtils.needsPassword(this.credentials)){
            CredentialsDialog credentialsDialog = new CredentialsDialog(myProject, serverURI);
            credentialsDialog.setCredentials(this.credentials);
            credentialsDialog.setAllowSavePassword(CredentialsManagerFactory.getCredentialsManager(IdeaPersistenceStoreProvider.INSTANCE)
                    .canWrite());

            if (UIHelpers.openOnUIThread(credentialsDialog) == DialogWrapper.CANCEL_EXIT_CODE){
                //TODO might need to update to see the task was cancelled.
                return;
            }
            this.credentials = credentialsDialog.getCredentials();
        }
        while(this.connection == null && !progressIndicator.isCanceled()){

            ConnectCommand connectCommand = getConnectCommand(this.serverURI, this.credentials);
            connectCommand.execute(progressIndicator);

            final Throwable exception = connectCommand.getException();

            if(connectCommand.finishedOk()){
                this.connection = connectCommand.getConnection();
            } else if (exception != null){
                if (exception instanceof TFSUnauthorizedException ||
                       exception instanceof ACSUnauthorizedException){
                    CredentialsDialog credentialsDialog = new CredentialsDialog(myProject, this.serverURI);
                    credentialsDialog.setErrorMessage(exception.getLocalizedMessage());
                    credentialsDialog.setCredentials(this.credentials);
                    credentialsDialog.setAllowSavePassword(CredentialsManagerFactory.getCredentialsManager(IdeaPersistenceStoreProvider.INSTANCE)
                            .canWrite());

                    if (UIHelpers.openOnUIThread(credentialsDialog) == DialogWrapper.OK_EXIT_CODE){
                        this.credentials = credentialsDialog.getCredentials();
                        continue;

                    }
                } else if ((!(exception instanceof TransportRequestHandlerCanceledException))){
                    log.warn("Unexpected connection exception", exception);
                    if (this.showErrorDialog){
                        UIHelpers.runOnUIThread(false, new Runnable() {
                            @Override
                            public void run() {
//                                Messages.showErrorDialog(myProject, MessageFormat.format(Messages.getString("ConnectTask.ConnectFailedMessageFormat"),
//                                                new Object[]{ serverURI.toASCIIString(), exception.getLocalizedMessage() }),
//                                        Messages.getString("TeamProjectSelectControl.ConnectionFailedDialogTitle"));
                                Messages.showErrorDialog(myProject, exception.getLocalizedMessage(),
                                        Messages.getString("TeamProjectSelectControl.ConnectionFailedDialogTitle"));
                            }
                        });
                    }
                    this.connection = null;
                    break;
                } else if (exception instanceof TransportRequestHandlerCanceledException)
                    throw (TransportRequestHandlerCanceledException)exception;
            }
        }

    }

    protected abstract ConnectCommand getConnectCommand(URI serverURI, Credentials credentials);

    public interface  ConnectCommand {
        TFSConnection getConnection();

        boolean finishedOk();

        Throwable getException();

        void execute(ProgressIndicator progress);
    }

    public final TFSConnection getConnection(){
        return this.connection;
    }
}
